<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title></title>
    <!-- 这里可以添加CSS样式文件 -->
  </head>

  <body>
    <!-- 渲染画布 -->
    <div id="box"></div>
    <script type="importmap">
      {
        "imports": {
          "three": "./threejs/build/three.module.js",
          "three/addons/": "./threejs/examples/jsm/"
        }
      }
    </script>
    <script type="module">
      /* ========= 1. 统一 importmap 已声明，这里直接 import ========= */
      import * as THREE from "three";
      import { OrbitControls } from "three/addons/controls/OrbitControls.js";
      import { FBXLoader } from "three/addons/loaders/FBXLoader.js";
      import GUI from "https://cdn.jsdelivr.net/npm/lil-gui@0.19/+esm"; // dat.gui 新版

      /* ========= 2. 模板里已经准备好的变量 ========= */
      const box = document.getElementById("box");
      const scene = new THREE.Scene();
      const camera = new THREE.PerspectiveCamera(
        75,
        box.clientWidth / box.clientHeight,
        0.1,
        100000
      );
      camera.position.set(150, 150, -700);

      const renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true,
        logarithmicDepthBuffer: true,
      });
      renderer.setSize(box.clientWidth, box.clientHeight);
      renderer.setClearColor(0x000000, 0);
      renderer.setPixelRatio(window.devicePixelRatio * 2);
      box.appendChild(renderer.domElement);

      const controls = new OrbitControls(camera, renderer.domElement);
      controls.enableDamping = true;

      /* 自适应大小 */
      window.addEventListener("resize", () => {
        renderer.setSize(box.clientWidth, box.clientHeight);
        camera.aspect = box.clientWidth / box.clientHeight;
        camera.updateProjectionMatrix();
      });

      /* 灯光 */
      scene.add(
        new THREE.DirectionalLight(0xffffff, 3.8),
        new THREE.AmbientLight(0xffffff, 2)
      );

      /* ========= 3. 其余逻辑全部照搬 ========= */
      let model = null;
      const HOST = ""; // 按实际路径填写
      new FBXLoader().load(HOST + "/files/model/city.FBX", (obj) => {
        scene.add(obj);
        obj.scale.set(0.04, 0.04, 0.04);
        obj.position.set(224, -9, -49);
        model = obj;
        modelBlendShader(obj);
      });

      /* 渲染循环 */
      function animate() {
        requestAnimationFrame(animate);
        controls.update();
        const time = performance.now() * 0.001;
        model && model.render?.(time);
        renderer.render(scene, camera);
      }
      animate();

      /* —————————— 着色器替换 —————————— */
      function modelBlendShader(model) {
        let materials = [];
        model.traverse((c) => c.isMesh && materials.push(c.material));
        materials = [...new Set(materials)];

        const params = {
          intensity: 1.2,
          colorScale: 1.5,
          animationSpeed: 0.8,
          baseColor: "#6edbe8",
          useTexture: true,
          textureMix: 0.7,
        };

        const uniforms = {
          iTime: { value: 0, unit: "float" },
          intensity: { value: params.intensity, unit: "float" },
          colorScale: { value: params.colorScale, unit: "float" },
          baseColor: { value: new THREE.Color(params.baseColor), unit: "vec3" },
          animSpeed: { value: params.animationSpeed, unit: "float" },
          useTexture: { value: params.useTexture, unit: "bool" },
          textureMix: { value: params.textureMix, unit: "float" },
        };

        const glslProps = {
          vertexHeader: `
                varying vec2 vUv;
                varying vec3 v_position;
                void main() {
                    vUv = uv;
                    v_position = position;
            `,
          fragHeader:
            Object.keys(uniforms)
              .map((k) => `uniform ${uniforms[k].unit} ${k};`)
              .join("\n") + "\nvarying vec3 v_position; varying vec2 vUv;\n",
          fragBody: `
                vec4 O = vec4(0.0);
                vec2 I = (vUv - 0.5) * 2.0;
                float i = 0.0, d, z = 0.0;
                for(O *= i; i++ < 1e2;
                     O += (cos(z + vec4(0,2,3,0)) + 1.5) / d / z * colorScale)
                {
                    vec3 p = z * normalize(vec3(I.x*2.0, I.y*2.0, -1.0)) + .8;
                    d = max(-p.y, 0.);
                    p.y += d+d - 1.;
                    z += d = .3 * (.01 + .1*d + length(min(p = cos(p + iTime * animSpeed) + cos(p/.6).yzx, p.zxy)) / ++d / d);
                }
                O = tanh(O / 7e2) * intensity;
                O.rgb = mix(O.rgb, O.rgb * vec3(baseColor), 0.5);
    
                vec4 diffuseColor = O;
                #ifdef USE_MAP
                    if(useTexture) {
                        vec3 textureColor = texture2D(map, vUv).rgb;
                        float luminance = dot(textureColor, vec3(0.299,0.587,0.114));
                        diffuseColor = mix(diffuseColor,
                                           diffuseColor * vec4(textureColor * (1.0+luminance), opacity),
                                           textureMix);
                    }
                #endif
            `,
        };

        materials.forEach((m) => {
          m.onBeforeCompile = (shader) => {
            Object.assign(shader.uniforms, uniforms);
            shader.vertexShader = shader.vertexShader.replace(
              "void main() {",
              glslProps.vertexHeader
            );
            shader.fragmentShader = shader.fragmentShader
              .replace(
                "#include <common>",
                glslProps.fragHeader + "\n#include <common>\n"
              )
              .replace(
                "vec4 diffuseColor = vec4( diffuse, opacity );",
                glslProps.fragBody
              );
            m.needsUpdate = true;
          };
        });

        /* GUI */
        setupGUI(params, uniforms);

        /* 时间更新 */
        model.render = (t) => (uniforms.iTime.value = t);
      }

      function setupGUI(p, u) {
        const gui = new GUI();
        gui.width = 300;
        const fx = gui.addFolder("着色器效果");
        fx.add(p, "intensity", 0.1, 3).onChange((v) => (u.intensity.value = v));
        fx.add(p, "colorScale", 0.5, 3).onChange(
          (v) => (u.colorScale.value = v)
        );
        fx.add(p, "animationSpeed", 0.1, 2).onChange(
          (v) => (u.animSpeed.value = v)
        );
        fx.addColor(p, "baseColor").onChange((v) => u.baseColor.value.set(v));
        const tex = gui.addFolder("纹理控制");
        tex.add(p, "useTexture").onChange((v) => (u.useTexture.value = v));
        tex
          .add(p, "textureMix", 0, 1)
          .onChange((v) => (u.textureMix.value = v));
        fx.open();
        tex.open();
      }
    </script>
  </body>
</html>
